#ifndef LOGAPPENDER_H_JM_20130121
#define LOGAPPENDER_H_JM_20130121

// ## c++
#include <iostream>
#include <vector>
#include <set>
#include <fstream>
// ## jmlib
#include "LoggerActivatorFilter.h"

namespace jmlib {


	class LogAppender {
	public: 

		enum LogFragmentType {LINE_NUMBER /*l*/, FUNCTION_NAME /*f*/, 
			LOGGER_ID /*i*/, LOG_LEVEL /*x*/, LOG_MESSAGE /*m*/, 
			LOG_TIME/*t*/, PLAIN_TEXT /***/, LOGGER_ADDRESS /*a*/};

		struct LogFragment {
			LogFragment(LogFragmentType t, const std::string& f = "")
				: fragmentType_(t) ,
				fragment_(f) { }

			LogFragmentType fragmentType_;
			std::string fragment_;
			
		};

		virtual std::ostream& getOutputStream() = 0;

		LogAppender(const std::string& format, const std::string& conf);
		virtual ~LogAppender() { }

		void parseLogFormat(const std::string& format);

		void dumpFragments(size_t from, size_t to);

		virtual void beforeLogging(const std::string& loggerName, 
			const std::string& logLevel, const char* functionName, 
			size_t lineNumber, const int loggerThis_);

		virtual void afterLogging();

		void addActivationFilter(LoggerActivatorFilter* f);

		bool isLoggerAccepted(const std::string& loggerName);

		void lockLog() { lockLog(true); }
		void unlockLog() { lockLog(false); }

		const std::string& getConfig() const { return config_; }
	private:
		const std::string config_;
		std::set<LoggerActivatorFilter*> activationFilters_;
		std::vector<LogFragment> fragments_;
		std::string loggerId_;
		std::string logLevel_;
		std::string functionName_;
		int loggerThis_;
		size_t lineNumber_;
		size_t logFragmentMessageIndex_;
		
		void lockLog(bool lock);
	};


	//////////////////////////////////////////////
	class StdCoutLogAppender : public LogAppender {
	public:
		StdCoutLogAppender(const std::string& format, const std::string& conf)
			: LogAppender(format, conf) { }
		virtual std::ostream& getOutputStream() override { return std::cout; }; 
	};

	class StdCerrLogAppender : public LogAppender {
	public:
		StdCerrLogAppender(const std::string& format, const std::string& conf)
			: LogAppender(format, conf) { }
		virtual std::ostream& getOutputStream() override { return std::cerr; }; 
	};

	//////////////////////////////////////////////

	class FileLogAppender : public LogAppender {
	public:
		FileLogAppender(const std::string& format, const std::string& conf)
			: LogAppender(format, conf) ,
			file_("log.txt"),
			append_(false) {

				initConfigs(conf);

				if (append_)
					stream_.open(file_, std::fstream::out | std::fstream::app);
				else 
					stream_.open(file_, std::fstream::out);
		}
		
		virtual ~FileLogAppender() {
			stream_.close();
		}
		virtual std::ostream& getOutputStream() override { return stream_; }; 


	private:
		std::fstream stream_;
		std::string file_;
		bool append_;

		void initConfigs(const std::string& confs);
		void initConfig(const std::string& conf);
	};
} // namespace jmlib

//typedef jmlib::StdCoutLogAppender jmlib_StdCoutLogAppender;
//typedef jmlib::StdCerrLogAppender jmlib_StdCerrLogAppender;
//typedef jmlib::FileLogAppender jmlib_FileLogAppender;
#endif
